using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using NetduinoHappyHour.Util;

namespace NetduinoHappyHour.Hardware
{
    public class NetduinoGateway : IRobotGateway
    {
        private static NetduinoGateway _instance = null;

        /// <summary>
        /// Private constructor to prevent multiple initialization
        /// </summary>
        private NetduinoGateway() { }
        
        public static NetduinoGateway Instance()
        {
            if (_instance == null)
            {
                _instance = new NetduinoGateway();
            }
            return _instance;
        }

        /// <summary>
        /// Executes a DriveCommand by delegating call to dCommand.Execute()
        /// </summary>
        /// <param name="dCommand">
        /// The drive command to be executed
        /// </param>
        public void ExecuteDriveCommand(DriveCommand dCommand)
        {
            dCommand.Execute();
        }


        /// <summary>
        /// Waits for pulling DIGITAL IO Pin to ground, could be used as a start command.
        /// Uses SecretLabs.NETMF.Hardware.Netduino.Pins.ONBOARD_SW1 for waiting
        /// </summary>
        public void WaitForOnboardButton()
        {
            WaitForPinOrButton(Pins.ONBOARD_SW1);
        }

        /// <summary>
        /// Waits for pulling DIGITAL IO Pin to ground, could be used as a start command.
        /// Use SecretLabs.NETMF.Hardware.Netduino.Pins.GPIO_PIN_Dx for easily adressing the right DIGITAL IO Pin
        /// Also the onboard button could be used to start
        /// </summary>
        /// <param name="waitPin">
        /// The pin to wait for
        /// </param>
        public void WaitForPinOrButton(Cpu.Pin waitPin)
        {
            InputPort buttonPort = null;
            InputPort pinPort = null;
            bool buttonState = false;
            bool pinState = false;

            buttonPort = new InputPort(Pins.ONBOARD_SW1, false, Port.ResistorMode.PullUp);
            // this is to prevent the same _serialPort is opened twice, because this would fail
            if (waitPin != Pins.ONBOARD_SW1)
            {
                pinPort = new InputPort(waitPin, false, Port.ResistorMode.PullUp);
            }
            while (true)
            {
                buttonState = !buttonPort.Read();

                if (waitPin != Pins.ONBOARD_SW1)
                {
                    pinState = !pinPort.Read();
                }
                if (buttonState || pinState)
                {
                    buttonPort.Dispose();
                    if (waitPin != Pins.ONBOARD_SW1)
                    {
                        pinPort.Dispose();
                    }
                    return;
                }
            }
        }

        /// <summary>
        /// Blinks onboard led infinitely, DOES NOT RETURN !
        /// </summary>
        public void BlinkInfinite()
        {
            OutputPort port = new OutputPort(Pins.ONBOARD_LED, false);
            while (true)
            {
                System.Threading.Thread.Sleep(250);
                port.Write(!port.Read());
            }
        }

        /// <summary>
        /// Blinks onboard led n-times
        /// </summary>
        public void BlinkShort()
        {
            OutputPort port = new OutputPort(Pins.ONBOARD_LED, false);
            int i = 0;
            while (10 > i++)
            {
                System.Threading.Thread.Sleep(250);
                port.Write(!port.Read());
            }
            port.Dispose();
        }
    }
}
